#include "job.h"
#include "getvoltages.h"

//persistJobHolder persistJobs;			// global declaration


Job::Job(string taskname, persistJobHolder &pjh)
// new constructor remove when new one works
{
	opt.fromMathematica(stdlink);
	sendmessage("entering New base job constructor","",opt.getL("messagelevel"));
	sendmessage("Here are the options: \\\\n",opt.toString("\\\\n","\\\\t"),opt.L["messagelevel"]);
	pjh.clearFinishedJobs();
	iErr = DAQmxCreateTask (taskname.c_str(), &taskHandle);
	checkerr(iErr,opt.getL("messagelevel"));
}
Job::Job(string taskname, persistJobHolder &pjh, option& optOld) : opt(optOld)
// copy option reference
{
	sendmessage("entering  base job constructor that copies options","",opt.getL("messagelevel"));
	sendmessage("Here are the options: \\\\n",opt.toString("\\\\n","\\\\t"),opt.L["messagelevel"]);
	pjh.clearFinishedJobs();

	string chanStr = opt.getS("channels");
	std::replace(chanStr.begin(), chanStr.end(), '/', 'X');  // from algorithm
	std::replace(chanStr.begin(), chanStr.end(), ',', 'Y');  // from algorithm
//	std::cout << "chanStr : " << chanStr << std::endl;
	taskname += chanStr;
	sendmessage("in Job constructor taskname: ", taskname, opt.getL("messagelevel"));
	iErr = DAQmxCreateTask (taskname.c_str(), &taskHandle);
	checkerr(iErr,opt.getL("messagelevel"));
	sendmessage("Leaving job ctor","", opt.getL("messagelevel"));
}
Job::Job(Job& old) : taskHandle(old.getTaskHandle()), opt(old.getOptions())
{
	old.clearTaskHandle();
}

Job::~Job()
{
// sending message is problematic beacuse mathlink is closed after data is returned;
// No point sending messages unless scope of Job in samller
//	sendmessage("Base class Job destructor called","",opt.getL("messagelevel"));
//	sendmessage("base dtor taskhandle = ",(long) taskHandle,opt.getL("messagelevel"));
	// Clear Task when destructor called is ok becaused cloned objects will hold task
	// and the original will only have taskHandle set to zero.
	if (taskHandle !=0)
	{
		iErr = DAQmxClearTask(taskHandle);
//		checkerr(iErr,getoptL("messagelevel"));
	}
//	checkerr(iErr,getoptL("messagelevel"));

}

int Job::startTask(TaskHandle th)
{
	sendmessage("in Job::startTask taskHandle is : ", (long) th ,getoptL("messagelevel"));
	iErr = DAQmxStartTask(th);
	checkerr(iErr, opt.getL("messagelevel"));
	return iErr;
}
void Job::continueTask(double arg1, double arg2)
{
	sendmessage("Job::continueTask should be overridden","!!!",getoptL("messagelevel"));
}
void Job::configureClock(long bufferSizeInSamples, double numPeriods)
//!    Configures the clock for both input and output.  In finite mode number of samples also
//! sets the buffer size.  The buffer size is not necessarily the same as the number
//! the user requested if sample mode is continuous this applies in triggered and buffered functions.
//!
{
	sendmessage("configure clock for input or output chan","!",getoptL("messagelevel"));
    sendmessage("      samples per channel to acquire", bufferSizeInSamples, getoptL("messagelevel"));
    sendmessage("      sammplemode =  ", getoptL("samplemode"), getoptL("messagelevel"));
	iErr = DAQmxCfgSampClkTiming (getTaskHandle(),
		getoptS("clock").c_str(),
		getoptD("frequency"),
		getoptL("activeedge"),
		getoptL("samplemode"),
		(long) bufferSizeInSamples * numPeriods	// intentional rounding to allow fractional periods
		);
	checkerr(iErr,getoptL("messagelevel"));
	return;
}

void InputJob::doConstruction()
{
	// new constructor
	sendmessage("inside NEW InputJob constructor using doConstruction","",getoptL("messagelevel"));
	sendmessage("messagelevel is ", getoptL("messagelevel"), getoptL("messagelevel"));
//	sampleArraySize = getoptL("samples") * getoptL("numchannels");
//	sampleArray = new double[sampleArraySize];
//    if (pDH ==0)
//        pDH = new dataHolder(0, getoptL("samples") * getoptL("numchannels")); //no pretrigger samples for buffered input jobs

    pData = new arrayHolder(DAQmx_Val_GroupByScanNumber, getoptL("samples"), getoptL("numchannels"));
	createAIChan();
}

InputJob::InputJob(string taskname, persistJobHolder& pjh) : Job(taskname, pjh)  //, pDH(pDataHolder)
{
	// new constructor
	doConstruction();
}
InputJob::InputJob(string taskname, persistJobHolder& pjh, option& optOld) : Job(taskname, pjh, optOld)  //, pDH(pDataHolder)
{
	// new constructor with options specified
	doConstruction();
}

InputJob::InputJob(InputJob& old) : Job(old)
// options should be initialized in Job copy constructor
{
//	sampleArraySize = getoptL("samples") * getoptL("numchannels");
//	sampleArray = new double[sampleArraySize];
//	for(int i =0; i < sampleArraySize; i++)					// probably don't need to copy elements
//		*(sampleArray + i) = *(old.getSampleArray() + i);

//    pDH = new dataHolder(0, getoptL("samples") * getoptL("numchannels"));
//    double* pNew = getPostTrigData();
//    double* pOld = old.getPostTrigData();
//    for(int i = 0; i < old.getPostTrigSize(); i++)
//        pNew[i] = pOld[i];

    pData = new arrayHolder(DAQmx_Val_GroupByScanNumber, getoptL("samples"), getoptL("numchannels"));
    double* pNewx = getPostTrigData();
    double* pOldx = old.getPostTrigData();
    for(int i = 0; i < old.getPostTrigSize(); i++)
        pNewx[i] = pOldx[i];

	// let destructor of old cleanup old
}
InputJob::~InputJob()
{
// sending message is problematic beacuse mathlink is closed after data is returned;
//	sendmessage("inside InputJob dtor","",getoptL("messagelevel"));
//	delete [] sampleArray;
//	delete pDH;
	delete pData;
}
void InputJob::continueTask(double arg1, double arg2)
{
	sendmessage("InputJob continuing Taskhandle = ",(long) getTaskHandle(),getoptL("messagelevel"));
//	isDoneNoError();

	readData();
	sendToMathematica();
	return;
}
void InputJob::createAIChan()
{
	sendmessage("creating AI voltage chan","!",getoptL("messagelevel"));
	iErr=DAQmxCreateAIVoltageChan(getTaskHandle(),
		getoptS("channels").c_str(),
		NULL,
		getoptL("terminalmode"),
		getoptD("min"),
		getoptD("max"),
		getoptL("units"),
		NULL);
	checkerr(iErr,getoptL("messagelevel"));
	return;
}

int InputJob::readData()
{
    //	bool32 fillMode = DAQmx_Val_GroupByChannel;
	bool32 fillMode = DAQmx_Val_GroupByScanNumber;		// changed to be consistent with continuous acquistions
	sendmessage("In readData using dataHolder, messagelevel= ", getoptL("messagelevel"), getoptL("messagelevel"));
    sendmessage("Data Holder sampleArraySize = ", getPostTrigSize(), getoptL("messagelevel"));
	int32 sampsPerChanRead=0;							// a return value

	iErr = DAQmxReadAnalogF64(
		getTaskHandle(),
		getoptL("samples"),
		getoptD("timeout"),
		fillMode,
		getPostTrigData(),
		getPostTrigSize(),
		&sampsPerChanRead,
		NULL
		);
	checkerr(iErr, getoptL("messagelevel"));
//	std::cout << "iErr: " << iErr << " samples read = " << sampsPerChanRead <<  std::endl;
//	std::cout << "some data: "<< getPostTrigData()[0] << " - " << getPostTrigData()[1] << std::endl;
//	DAQmxStopTask(getTaskHandle());				// should this be here stops automatically if reads autostart
	return 0;
}

bool InputJob::sendToMathematica()
{
	sendmessage("In Input::Job::sendToMathematica dataholder post trig size = ", getPostTrigSize(), getoptL("messagelevel"));
	if (MLReady(stdlink))
	{
		MLNewPacket(stdlink);
		MLNewPacket(stdlink);		// second one didn't help
		MLPutString(stdlink,"An error occurred reading the MathLink Connection");
	}
	else
	{
//		What should single measurement return list of one element or Real Number
// 			What about a MaxFlatten Attribute??
//		sendmessage("sampleArraySize =", sampleArraySize,3);
//		if (getoptL("numchannels") == 1 && getoptL("samples") == 1)
//			MLPutReal(stdlink, sampleArray[0]);
//		else
			MLPutRealList(stdlink, getPostTrigData(), getPostTrigSize());
	}
//	MLPutString(stdlink,"All Done");
	return true;
}
void InputJob::setTransferMech()
{
	sendmessage("setting Input transfermechanism = ","!", getoptL("messagelevel"));
	int32 mech =-1;

	iErr = DAQmxSetAIDataXferMech(
		getTaskHandle(),
		getoptS("channels").c_str(),			// transfer mechanism must be same for all channels
		getoptL("transfermech")
		);
	checkerr(iErr, getoptL("messagelevel"));
    iErr = DAQmxGetAIDataXferMech(getTaskHandle(),"dev1/ai0",&mech);
	checkerr(iErr, getoptL("messagelevel"));
	if (getoptD("frequency") < 20000.0 && getoptL("transfermech") == DAQmx_Val_Interrupts){
        iErr = DAQmxSetAIDataXferReqCond(getTaskHandle(), getoptS("channels").c_str(), DAQmx_Val_OnBrdMemNotEmpty);
        checkerr(iErr, getoptL("messagelevel"));
	}

//	iErr = DAQmxGetAIDataXferMech(getTaskHandle(),"dev1/ai1",&mech);
//	checkerr(iErr, getoptL("messagelevel"));
	return;
}


BufferedInputJob::BufferedInputJob(string taskname, persistJobHolder& pjh) : InputJob(taskname, pjh)
{
	// new constructor
	sendmessage("taskname is taskname --",  taskname, 1.0);
//	sendmessage("inside bufferdInputJob constructor","",getoptL("messagelevel"));
//	sendmessage("messagelevel is ", getoptL("messagelevel"), getoptL("messagelevel"));
//	configureClock(getoptL("samples"));
//	setTransferMech();
    doConstruction();
}
BufferedInputJob::BufferedInputJob(string taskname, persistJobHolder& pjh, option& optOld) : InputJob(taskname, pjh, optOld)
{
	// new constructor
//	sendmessage("inside buffered InputJob constructor","",getoptL("messagelevel"));
//	sendmessage("messagelevel is ", getoptL("messagelevel"), getoptL("messagelevel"));
//	configureClock(getoptL("samples"));
//	setTransferMech();
    doConstruction();
}

void BufferedInputJob::doConstruction()
{
	// new constructor
	sendmessage("inside bufferdInputJob constructor","",getoptL("messagelevel"));
	sendmessage("messagelevel is ", getoptL("messagelevel"), getoptL("messagelevel"));
	configureClock(getoptL("samples"));
	setTransferMech();


}

BufferedInputJob::~BufferedInputJob()
{
// sending message is problematic beacuse mathlink is closed after data is returned;
//	sendmessage("inside InputJob dtor","",getoptL("messagelevel"));
}

void BufferedInputJob::continueTask(double arg1, double arg2)
{
	sendmessage("BufferedInputJob continuing Taskhandle = ",(long) getTaskHandle(),getoptL("messagelevel"));
	startTask(getTaskHandle());
	isDoneNoError();
	readData();
	sendToMathematica();
	return;
}
bool BufferedInputJob::isDoneNoError()
{
	sendmessage("In BufferedInputJob isDoneNoError, taskhandle= ", getoptL("taskHandle"), getoptL("messagelevel"));
	volatile bool32 taskDone = false;
	TaskHandle tH = getTaskHandle();
	volatile int iErr = 0;
	while(!taskDone && !iErr && !MLAbort)
	{
		iErr = DAQmxIsTaskDone (tH, (bool32*) &taskDone);
//		checkerr(iErr,5);
//		sendmessage("isTaskDone = ",taskDone,5);		// inner loop
//		sendmessage("MLAbort = ", MLAbort,5);
		if (MLAbort)
		{
			DAQmxClearTask(tH);
			MLNewPacket(stdlink);
			MLPutFunction(stdlink,"Abort",0);
			return false;
		}
	}
	if (iErr == 0)
		return true;
	return false;
}

TrigJob::TrigJob(string taskname, persistJobHolder& pjh) : InputJob(taskname, pjh)
{return; /*doConstruction();*/ }
TrigJob::TrigJob(string taskname, persistJobHolder& pjh, option& tempOpts) : InputJob(taskname, pjh, tempOpts)
{; /*doConstruction();*/ }
TrigJob::~TrigJob()
{
	; //delete [] pTestingArray;				/
}

SoftTrigJob::SoftTrigJob(string taskname, persistJobHolder& pjh) : TrigJob(taskname, pjh)
{doConstruction();}
SoftTrigJob::SoftTrigJob(string taskname, persistJobHolder& pjh, option& tempOpts) : TrigJob(taskname, pjh, tempOpts)
{doConstruction();}
void SoftTrigJob::doConstruction()
//! "Samples" parameter from mathematica is same as pretriggersamples
//! "Pretrigger samples is an option from Mathemmatica as well
//! Configure clocks sets DAQ cards buffer size
//!
{
  	long preSize = getoptL("pretrigsamples") * getoptL("numchannels");
 	long postSize = getoptL("posttrigsamples") * getoptL("numchannels");

// 	dataHolder* oldpDH = pDH;
//    pDH = new dataHolder(preSize, postSize); // design error to use samples as pretrigger value
//	delete oldpDH;

	preData = new arrayHolder(DAQmx_Val_GroupByScanNumber, getoptL("pretrigsamples"), getoptL("numchannels"));
	postData = new arrayHolder(DAQmx_Val_GroupByScanNumber, getoptL("posttrigsamples"), getoptL("numchannels"));

//	pPreTrigArray = getPostTrigData();		// really just renaming for convenience
//	preTrigArraySize = getoptL("pretrigsamples") * getoptL("numchannels");	// InputJob will delete array
    long testSize = 10000 * getoptL("numchannels");	// 10,000 if slow
	//testingArraySize = 10000 * getoptL("numchannels");	// 10,000 if slow
	if (getoptD("frequency") > 9999)
		testSize = 100000 * getoptL("numchannels");	// 100,000 if fast
//	uInt32 postTrigArraySize = getoptL("numchannels") * getoptL("posttrigsamples");
	if ((postSize + preSize) > testSize)
		testSize = postSize + preSize;
//	pTestingArray = new float64[testSize];
    testData = new arrayHolder(DAQmx_Val_GroupByScanNumber, testSize/getoptL("numchannels"), getoptL("numchannels"));

//	testingArraySize = testSize;  // temp
	readPastTrig = 0;


//    configureClock((postSize + preSize)/getoptL("numchannels")); // if too large slows down acquisision
    configureClock(getoptL("pretrigsamples")+ getoptL("posttrigsamples")); // if too large slows down acquisision
	setTransferMech();
	return;
}

SoftTrigJob::~SoftTrigJob()
{
//	delete [] pTestingArray;				// input job will clean up pretrigger array
	delete preData;
	delete postData;
	delete testData;
}

//class TrigTest
//{
////* modifies triggered and softwareTrig::readpast trigger
//// should this be a method in SoftTrig job?
//// note how I am using reference to modify private data member
//public:
//    TrigTest(SoftTrigJob& stj, long& rpt) :    data(stj.getTestingArray()),
//                                    trigLevel(stj.getoptD("triggerlevel")),
//                                    numChannels(stj.getoptL("numchannels")),
//                                    triggerenabled(false),
//                                    samplesAvailable(0),
//                                    preTrigSamples(stj.getoptL("pretrigsamples")),
//                                    readPastTrig(rpt)
//                                    {
//                                        //sendmessage("triggerslope option is ", stj.getoptL("triggerslope"));
//                                        if (stj.getoptL("triggerslope") == DAQmx_Val_Falling)
//                                            trigSlope = -1.0;
//                                        else
//                                            trigSlope = 1.0;
//                                    }
//
//   inline bool IsTriggered(int32 samplesInBuffer)
//    {
//        for(int i=0, j=0; i < samplesInBuffer; i++, j += numChannels)
//        {
//            if (trigSlope * (data[j] - trigLevel) < 0)
//                triggerenabled = true;
//            if (triggerenabled && (trigSlope * (data[j] - trigLevel) > 0)
//                    && samplesAvailable + i > preTrigSamples)
//            {
//                readPastTrig = samplesInBuffer - i;
//                return true;
//            }
//        }
//        samplesAvailable += samplesInBuffer;
//        return false;
//    }
//
//private:
//    double* data;
//    double trigLevel;
//    double trigSlope;
//    long numChannels;
//    long& readPastTrig;
//    bool triggerenabled;
//    long samplesAvailable;
//    long preTrigSamples;
//};


long SoftTrigJob::waitForTrigger()
//!
//! Waits to enable trigger until signal is below the triggerlevel and enough
//! samples to fill the pretrigger buffer have been taken
//! DAQmxReadAnalogF64 reads all available data in buffer
//!
{
	sendmessage("inside software triggering waiting for trigger","!",5);
	int32 samplemode = 0;
	DAQmxGetSampQuantSampMode(getTaskHandle(), &samplemode);
    sendmessage("wait for trigger samplemode from task:", samplemode ,5);

	TrigTest tt(*this, readPastTrig);
	TriggerContext tc(getoptL("pretrigsamples"), getoptL("posttrigsamples"),
                   getoptL("triggerslope"), getoptD("triggerlevel"), getoptD("triggerlevel"),
                   getChanPointerTestData(0), getoptL("numChannels"), getTestingArraySize());

	bool32	taskDone = false;
	bool32 fillMode = DAQmx_Val_GroupByScanNumber;
	int32 sampsPerChanRead=0;
	bool triggered = false;
	iErr = 0;

	while(!taskDone && !iErr && !triggered && !MLAbort)
	{
		iErr = DAQmxIsTaskDone(getTaskHandle(), &taskDone);
//		checkerr(iErr,5);
		iErr = DAQmxReadAnalogF64(getTaskHandle(), -1, getoptD("timeout"), fillMode, \
		getTestingArray(), getTestingArraySize(), &sampsPerChanRead, NULL);
//		checkerr(iErr,5);
//		sendmessage("pretrigger value read = ",pPreTrigArray[0]);

      //  UpdateTriggerState(pTestingArray, getoptD("triggerlevel"), 1, getoptL("numchannels"), sampsPerChanRead,
       //                                     triggered, readPastTrig);
       //triggered = tt.IsTriggered(sampsPerChanRead);
       sendmessage("samples read: ", sampsPerChanRead, getoptL("messagelevel"));
       triggered = tc.IsTriggered(sampsPerChanRead);
	}
	return triggered ? (readPastTrig = (sampsPerChanRead - tc.readPastTrig)) : readPastTrig = -1;
}
void SoftTrigJob::readPreTrigger()
{
	bool32 fillMode = DAQmx_Val_GroupByScanNumber;			// make class data memeber?
	uInt64 readPosition=0;
	int32 sampsPerChanRead=0;
//	std::cout << "reading pretrigger: readPastTrig = " << readPastTrig << std::endl;
	long  preTrigOffset = getoptL("pretrigsamples") + readPastTrig;
	DAQmxGetReadCurrReadPos(getTaskHandle(),&readPosition);
//	if (readPosition < preTrigOffset)
//	{
//		preTrigOffset = readPosition;
//		preTrigArraySize = (readPosition-readPastTrig) * getoptL("numchannels");  //FIX Should be unnecessary will pretrig always aquiring samples
//	}

	iErr = DAQmxSetReadOffset(getTaskHandle(), -1*preTrigOffset);
	checkerr(iErr, getoptL("messagelevel"));
	iErr = DAQmxReadAnalogF64 (getTaskHandle(), -1, getoptD("timeout"), fillMode, \
			getPreTrigArray(), getPreTrigArraySize(), &sampsPerChanRead, NULL);
	checkerr(iErr, getoptL("messagelevel"));
 //   std::cout << "PREtrigger samples read = " << sampsPerChanRead <<std::endl;
}
void SoftTrigJob::readPostTrigger()
{
//! read in the data from on card buffer to the posttrigger array
	int index = 0;
	bool32 fillMode = DAQmx_Val_GroupByScanNumber;			// make class data memeber?
//	uInt32 postTrigArraySize = getoptL("numchannels") * getoptL("posttrigsamples");
	int32 sampsPerChanRead=0;
	DAQmxSetReadOffset(getTaskHandle(), 0);
	index += sampsPerChanRead;
	iErr = DAQmxReadAnalogF64 (getTaskHandle(), -1, getoptD("timeout"), fillMode, \
			getPostTrigData(), getPostTrigSize(), &sampsPerChanRead, NULL);

	DAQmxSetReadOffset(getTaskHandle(), 0);
	while(!iErr && index < getoptL("posttrigsamples"))
	{
		index += sampsPerChanRead;
		iErr = DAQmxReadAnalogF64 (getTaskHandle(), -1, getoptD("timeout"), fillMode, \
				getPostTrigData() + index * getoptL("numchannels"), getPostTrigSize() - (index * getoptL("numchannels")),
				&sampsPerChanRead, NULL);
//        std::cout << "Posttrigger samples read = " << sampsPerChanRead <<std::endl;
//		sendmessage("Posttrigger samples read = ", sampsPerChanRead);
	}

	checkerr(iErr, getoptL("messagelevel"));

//	sendmessage("readpasttrig on the last testing pass = ", readpasttrig,opt.L["messagelevel"]);
//	sendmessage("Total number recorded before trigggering = ",(long) totalsamples,opt.L["messagelevel"]);
//	sendmessage("readposition = ",(long) readPosition,opt.L["messagelevel"]);
//	sendmessage("preTrigOffset = ",(long) preTrigOffset,opt.L["messagelevel"]);
}
bool SoftTrigJob::sendToMathematica()
{
	uInt32 postTrigArraySize = getoptL("numchannels") * getoptL("posttrigsamples");
	MLPutFunction(stdlink,"List",2);
		MLPutRealList(stdlink,getPreTrigArray(), getPreTrigArraySize());
//		MLPutRealList(stdlink, pTestingArray, getPostTrigSize());      //use if testing array and posttrigger are the same
       MLPutRealList(stdlink,getPostTrigData(), getPostTrigSize());  // use with data in posttrig array
	return true;
}
// needs triggersorce and triggerslope in the options
DigitalTrigJob::DigitalTrigJob(string taskname, persistJobHolder& pjh) : TrigJob(taskname, pjh)
{	doConstruction();}
DigitalTrigJob::DigitalTrigJob(string taskname, persistJobHolder& pjh, option& tempOpts) : TrigJob(taskname, pjh, tempOpts)
{doConstruction();}
void DigitalTrigJob::doConstruction()
{
	sendmessage("Entering DigitalTrigJob constructor","!",getoptL("messagelevel"));
//	iErr = DAQmxCfgDigEdgeStartTrig(getTaskHandle(),getoptS("triggersource").c_str(),getoptL("triggerslope"));		// should be DAQmxCfgDigEdgeRefTrig for pretrigger samples
	iErr = DAQmxCfgDigEdgeRefTrig(getTaskHandle(),getoptS("triggersource").c_str(), getoptL("triggerslope"), getoptL("pretrigsamples"));		// should be DAQmxCfgDigEdgeRefTrig for pretrigger samples
	checkerr(iErr,getoptL("messagelevel"));
	long tempArraySize = (getoptL("pretrigsamples") + getoptL("posttrigsamples")) * getoptL("numchannels");
    testData = new arrayHolder(DAQmx_Val_GroupByScanNumber, getoptL("pretrigsamples") + getoptL("posttrigsamples"), getoptL("numchannels"));
    configureClock(getoptL("pretrigsamples") + getoptL("posttrigsamples"));  //***** Check
	setTransferMech();
return;
}
//DigitalTrigJob::DigitalTrigJob(DigitalTrigJob& old) : InputJob(old)
//{		//no data memebers
//}
DigitalTrigJob::~DigitalTrigJob()
{
    delete testData;
}

int DigitalTrigJob::readData()
{
    // bool32 fillMode = DAQmx_Val_GroupByChannel;      //
	bool32 fillMode = DAQmx_Val_GroupByScanNumber;		// changed to be consistent with continuous acquistions
	sendmessage("In readData , messagelevel= ", getoptL("messagelevel"), getoptL("messagelevel"));
    sendmessage("ArraySize = ", getTestSize(), getoptL("messagelevel"));
	int32 sampsPerChanRead=0;							// a return value
	iErr = DAQmxReadAnalogF64(
		getTaskHandle(),
		getoptL("pretrigsamples") + getoptL("posttrigsamples"),
		getoptD("timeout"),
		fillMode,
		getTestData(),
		getTestSize(),
		&sampsPerChanRead,
		NULL
		);
	checkerr(iErr,getoptL("messagelevel"));
    sendmessage("Samples per channel read = ", sampsPerChanRead, getoptL("messagelevel"));
//	DAQmxStopTask(getTaskHandle());				// should this be here stops automatically if reads autostart
	return 0;
}

bool DigitalTrigJob::sendToMathematica()
{
	sendmessage("In Temp DigitalTrigJob::sendToMathematica  test size = ", getTestSize(), getoptL("messagelevel"));
    sendmessage("     pretrigsamples = ", getoptL("pretrigsamples"), getoptL("messagelevel"));
    sendmessage("     posttrigsamples = ", getoptL("posttrigsamples"), getoptL("messagelevel"));

	if (MLReady(stdlink))
	{
		MLNewPacket(stdlink);
		MLNewPacket(stdlink);		// second one didn't help
		MLPutString(stdlink,"An error occurred reading the MathLink Connection");
	}
	else
	{
//		What should single measurement return list of one element or Real Number
// 			What about a MaxFlatten Attribute??
//		sendmessage("sampleArraySize =", sampleArraySize,3);
//		if (getoptL("numchannels") == 1 && getoptL("samples") == 1)
//			MLPutReal(stdlink, sampleArray[0]);
//		else
			//MLPutRealList(stdlink, getTestData(), getTestSize());
        MLPutFunction(stdlink,"List",2);
            MLPutRealList(stdlink,getTestData(), getoptL("pretrigsamples") * getoptL("numchannels"));
            MLPutRealList(stdlink,getTestData() + getoptL("pretrigsamples")*getoptL("numchannels") , getoptL("posttrigsamples") * getoptL("numchannels"));  // need to calculate pointer offset
	}
//	MLPutString(stdlink,"All Done");
	return true;
}


OutputJob::OutputJob(string taskname, persistJobHolder &pjh) : Job(taskname, pjh)
{doConstruction();}
OutputJob::OutputJob(string taskname, persistJobHolder &pjh, option& tempOptions) : Job(taskname, pjh, tempOptions)
{doConstruction();}

void OutputJob::doConstruction()
{
	dataStorage[0] = 0.0;
	dataStorage[1] = 0.0;
	sendmessage("inside new OutputJob constructor","",getoptL("messagelevel"));
	sendmessage("messagelevel is ", getoptL("messagelevel"), getoptL("messagelevel"));
	createAOChan();
//	configureClock(getoptD("numperiods"));		// need samples to be samples*numperiods for output only
//	setTransferMech();
//	waveformToBuffer();
}
OutputJob::OutputJob(OutputJob& old) : Job(old)
{
	dataStorage[0] = old.dataStorage[0];
	dataStorage[1] = old.dataStorage[1];
	// job copy constructor clears the old taskhandle
}

OutputJob::~OutputJob()
{
	//sending message is problematic beacuse mathlink is closed after data is returned;
//	sendmessage("inside OutputJob dtor","",getoptL("messagelevel"));
		;
}

void OutputJob::continueTask(double ch0Volt, double ch1Volt)
{
	dataStorage[0] = ch0Volt;
	dataStorage[1] = ch1Volt;
	writeData();
	MLPutSymbol(stdlink,"Null");
}
void OutputJob::writeData()
{
	bool32 autoStart = false;
	int32 written;
	sendmessage("writedata = dataStorage[0] = ", dataStorage[0], getoptL("messagelevel"));
	sendmessage("writedata = dataStorage[1] = ", dataStorage[1], getoptL("messageLevel"));
	iErr = DAQmxWriteAnalogF64(
		getTaskHandle(),
		1,				//better be 1
		autoStart,
		getoptD("timeout"),
		DAQmx_Val_GroupByChannel,		// change?
		dataStorage,
		&written,
		NULL);
	checkerr(iErr, getoptL("messagelevel"));
	return;
}

bool OutputJob::sendToMathematica()
{
	if (MLReady(stdlink))
	{
		MLNewPacket(stdlink);
		MLNewPacket(stdlink);		// second one didn't help
		MLPutString(stdlink,"An error occurred reading the MathLink Connection");
	}
	else
	{
//		sendmessage("sampleArraySize =", sampleArraySize,3);
		MLPutLongInteger(stdlink, (long) getTaskHandle());
	}
	return true;
}
void OutputJob::createAOChan()
{
	sendmessage("creating AO voltage chan","!",getoptL("messagelevel"));
	iErr=DAQmxCreateAOVoltageChan(getTaskHandle(),
		getoptS("channels").c_str(),
		NULL,
//		getoptL("terminalmode"),
		getoptD("min"),
		getoptD("max"),
		getoptL("units"),
		NULL);
	checkerr(iErr,getoptL("messagelevel"));
	return;
}

BufferedOutputJob::BufferedOutputJob(string taskname, persistJobHolder& pjh) : OutputJob(taskname, pjh)
{ doConstruction();}

BufferedOutputJob::BufferedOutputJob(string taskname, persistJobHolder& pjh, option& tempOpts) : OutputJob(taskname, pjh, tempOpts)
{doConstruction();}

BufferedOutputJob::BufferedOutputJob(BufferedOutputJob& old) : OutputJob(old)
{	;// no data members
}

BufferedOutputJob::~BufferedOutputJob()
{	; // nothing special to do
}
void BufferedOutputJob::doConstruction()
{
	configureClock(getoptL("samples"), getoptD("numperiods"));		// need samples to be samples*numperiods for output only
	setTransferMech();
	waveformToBuffer();
}
void OutputJob::setTransferMech()
{
	sendmessage("setting output transfermechanism = ","!", getoptL("messagelevel"));
	int32 mech = -1;

	iErr = DAQmxSetAODataXferMech(
		getTaskHandle(),
		getoptS("channels").c_str(),			// more than one chan ok? yes all chans must have same transfer mech
		getoptL("transfermech")
		);
	checkerr(iErr, getoptL("messagelevel"));

// *** need to fix this to just check the channels being changed or defined
//	uInt32 bufSize=200;
//	char chans[200];
//	iErr = DAQmxGetTaskChannels(getTaskHandle(),chans,bufSize);
//	sendmessage("Output task number", chans, getoptL("messagelevel")+5);

//	iErr = DAQmxGetAODataXferMech(getTaskHandle(),"dev1/ao0",&mech);
//	checkerr(iErr, getoptL("messagelevel"));
//	sendmessage("setting Output transfermechanism ao0 == ", mech, getoptL("messagelevel"));
//	iErr = DAQmxGetAODataXferMech(getTaskHandle(),"dev1/ao1",&mech);
	checkerr(iErr, getoptL("messagelevel"));
	sendmessage("error number is ", iErr, getoptL("messagelevel"));
	sendmessage("setting Out transfermechanism ao1 == ", mech, getoptL("messagelevel"));
	return;
}
void BufferedOutputJob::waveformToBuffer()
{
	bool autoStart = false;
	int32 written;
	iErr = DAQmxWriteAnalogF64(
		getTaskHandle(),
		getoptL("samples"),
		autoStart,
		getoptD("timeout"),
		DAQmx_Val_GroupByChannel,		// change?
		getoptDArray("waveform"),
		&written,
		NULL);
	checkerr(iErr, getoptL("messagelevel"));
	return;
}
void BufferedOutputJob::continueTask(double ch0Volt, double ch1Volt)
{
	bool32 ans = false;
	DAQmxStopTask(getTaskHandle());
	int iErr = DAQmxIsTaskDone(getTaskHandle(),&ans);
	checkerr(iErr,getoptL("messagelevel"));
	sendmessage("Is buffered text done ",ans,getoptL("messagelevel"));
	startTask(getTaskHandle());
	MLPutSymbol(stdlink,"Null");
}

persistJobHolder::~persistJobHolder()
{
	for(map<TaskHandle, JobState>::iterator ip = jobList.begin(); ip != jobList.end(); ++ip)
		delete ip->second.pJob;
}
Job& persistJobHolder::changeToPersist(Job &old, long runAgainCount)
{
//	m.okToCopy = true;			// do I want this check
	TaskHandle th = old.getTaskHandle();
	jobList[th].runAgainCount = runAgainCount;
	jobList[th].pJob = old.clone();
	return *(jobList[th].pJob);
}
void persistJobHolder::clearJob(TaskHandle task)
{
	sendmessage("in clear task id is ",(long)task, jobList[task].pJob->getoptL("messagelevel"));
	delete jobList[task].pJob;			// call objects destructor
	jobList.erase(jobList.find(task));
}
void persistJobHolder::clearFinishedJobs()
{
//	sendmessage("Entering persistjobholder clear finished jobs","!",5);  not associtated with an one job's message level
	typedef map<TaskHandle, JobState>::iterator jobList_iter;
	jobList_iter ip = jobList.begin();
	while(ip != jobList.end())
	{
		bool32 taskDone = false;
		jobList_iter old = ip;
		ip++;
		TaskHandle task = old->second.pJob->getTaskHandle();
		int iErr = DAQmxIsTaskDone(task, &taskDone);
//		sendmessage("in persistjob task id is ",(long)task,ip->second.pJob->getoptL("messagelevel");
//		checkerr(iErr, 5);
		if (old->second.runAgainCount == 0 && taskDone)
		{
			delete old->second.pJob;
			jobList.erase(old);
		}
	}
//	sendmessage("Exiting persistjobholder clear finished jobs","!",5);
}

//arrayHolder::arrayHolder(long groupingMethod, long samplesPerChannel, int numberOfChannels) :
//     groupMethod(groupingMethod), data(new double[samplesPerChannel * numberOfChannels]),
//     size(samplesPerChannel * numberOfChannels),
//     samplesPerChan(samplesPerChannel), numChans(numberOfChannels) {}
//
//arrayHolder::arrayHolder(long size, int numberOfChannels) : size(size), numChans(numberOfChannels)
//    {data = new double[size];}
//
//arrayHolder::~arrayHolder()
//    {delete[] data;}

//AddressCalculator::AddressCalculator(arrayHolder& ah, int channel) : offset(0), numChans(ah.getNumChans()), addr(0)
//{
//    if (ah.getGroupMethod() == DAQmx_Val_GroupByScanNumber)
//    {
//        offset = ah.getData() + channel;
//        addr = &AddressCalculatorHelper::addrByScan;
//    }
//    else   // DAQmx_Val_GroupByChannel
//    {
//        offset = ah.getData() + channel * ah.getSampsPerChan();
//        addr = &AddressCalculatorHelper::addrByChannel;
//    }
//}
